
#include "datum.h"



//////////////////////////////////////////////////////////////////////////  MYSQL EXCEPTION  //////////////////////////////////////////////////////////////////////////
DatumException::DatumException( void ) {
  return;
}

DatumException::DatumException( const std::string& file, const unsigned long& line, const std::string& extra ) {
  char* foo = new char[extra.size()+100];
  sprintf( foo, "Datum Exception %s line %lu: %s\n", file.c_str(), line, extra.c_str() );
  setMessage( foo );
  return;
}

DatumException::DatumException( const DatumException& ref ) {
  setMessage( ref.getMessage() );
  return;
}

DatumException::~DatumException( void ) {
  return;
}



//////////////////////////////////////////////////////////////////////////  DATUM CLASS  //////////////////////////////////////////////////////////////////////////
Datum::Datum( void ) {
  type( DATUM_TYPE_EMPTY );
  data( NULL );
  return;
}

Datum::Datum( const Datum& ref ) {
  data( NULL );
  copy( ref );
  return;
}

Datum::Datum( const signed int& value ) {
  data( NULL );
  set( value );
  return;
}

Datum::Datum( const char& value ) {
  data( NULL );
  set( value );
  return;
}

Datum::Datum( const char* value ) {
  data( NULL );
  set( value );
  return;
}

Datum::~Datum( void ) {
  realloc( 0 );
  return;
}

void Datum::copy( const Datum& ref ) { // private

  // Empty logic...
  if ( ref.type() == DATUM_TYPE_EMPTY ) {
    type( DATUM_TYPE_EMPTY );
    realloc( 0 );
    return;
  }

  // Array logic...
  if ( ref.type() == DATUM_TYPE_ARRAY ) {
    realloc( sizeof(std::list<Datum>*) );
    std::list<Datum>* l = new std::list<Datum>();
    std::list<Datum>* rl = ((std::list<Datum>*)ref.data());
    std::list<Datum>::const_iterator end = rl->end();
    for ( std::list<Datum>::const_iterator it = rl->begin(); it != end; ++it ) {
      l->push_back( *it );
    }
    data( l );
    type( DATUM_TYPE_ARRAY );
    return;
  }

  // Standard logic...
  size_t size = sizeof(ref.data());
  if ( ref.type() == DATUM_TYPE_STRING ) {
    ++size; // Tack an extra byte on for the null-terminatorstrings
  }
  type( ref.type() );
  realloc( size );
  if ( size ) {
    memcpy( data(), ref.data(), size );
  }

  return;
}

void Datum::realloc( const size_t& size ) { // private
  std::list<Datum>* l = NULL;

  // Special array preprocessing...
  if ( type() == DATUM_TYPE_ARRAY ) {
    l = (std::list<Datum>*)data();
    l->clear();
    delete l;
    data( NULL );
  }

  // Special string preprocessing...
  if ( type() == DATUM_TYPE_STRING ) {
    free( data() );
    data( NULL );
  }

  // Get on with it already...
  data( ::realloc( data(), size ) );

  return;
}

void Datum::make_array( void ) { // private
  std::list<Datum>* l = NULL;

  // If it's already an array, we've got no work to do...
  if ( type() == DATUM_TYPE_ARRAY ) {
    return;
  }

  l = new std::list<Datum>();
  l->push_back( *this );
  realloc( sizeof(std::list<Datum>*) );
  data( l );
  type( DATUM_TYPE_ARRAY );

  return;
}

std::list<Datum>::iterator Datum::get_pos( const unsigned int& index ) {
  std::list<Datum>* l = NULL;
  std::list<Datum>::iterator it;
  unsigned long u = 0;

  // Get ourselves set up...
  make_array();
  l = (std::list<Datum>*)data();

  // Check for an out-of-bounds...
  if ( index >= l->size() ) {
    throw DatumException( __FILE__, __LINE__, "Array index out of bounds" );
  }

  if ( index <= l->size()/2 ) {
    // Count from the beginning...
    it = l->begin();
    u = 0;
    while ( u < index ) {
      ++it;
      ++u;
    }
  } else {
    // Count from the end...
    it = l->end();
    u = l->size();
    while ( u > index ) {
      --it;
      --u;
    }
  }

  return it;
}

void Datum::set( const signed int& value ) { // private
  size_t size = sizeof(value);
  realloc( size );
  memcpy( data(), &value, size );
  type( DATUM_TYPE_SIGNED_INT );
  return;
}

void Datum::set( const char& value ) { // private
  size_t size = sizeof(value);
  realloc( size );
  memcpy( data(), &value, size );
  type( DATUM_TYPE_CHAR );
  return;
}

void Datum::set( const char* value ) { // private
  size_t size = strlen(value)+1;
  realloc( size );
  strcpy( (char*)data(), value );
  type( DATUM_TYPE_STRING );
  return;
}

Datum& Datum::operator = ( const Datum& ref ) { // public
  copy( ref );
  return *this;
}

Datum& Datum::operator = ( const signed int& value ) { // public
  set( value );
  return *this;
}

Datum& Datum::operator = ( const char& value ) { // public
  set( value );
  return *this;
}

Datum& Datum::operator = ( const char* value ) { // public
  set( value );
  return *this;
}

/*    3 cases:
 * 1: this is an array already (append the new value)
 * 2: this is not an array, but a value (result is a list of current value and new value)
 * 3: this is not an array, and empty (result is a list of just the new value)
 */
Datum& Datum::append( const Datum& ref ) { // public
  make_array();
  ((std::list<Datum>*)data())->push_back( ref );
  return *this;
}

Datum& Datum::at( const unsigned long& index ) { // public
  return *get_pos( index );
}

Datum& Datum::insert( const Datum& ref, const unsigned long& index ) { // public
  std::list<Datum>::iterator pos = get_pos( index );
  ((std::list<Datum>*)data())->insert( pos, ref );
  return *this;
}

Datum& Datum::remove( const unsigned long& index ) { // public
  ((std::list<Datum>*)data())->erase( get_pos( index ) );
  return *this;
}

Datum& Datum::first( void ) { // public
  make_array();
  return ((std::list<Datum>*)data())->front();
}

Datum& Datum::last( void ) { // public
  make_array();
  return ((std::list<Datum>*)data())->back();
}

Datum& Datum::reverse( void ) { // public
  make_array();
  ((std::list<Datum>*)data())->reverse();
  return *this;
}

void Datum::clear( void ) { // public
  realloc( 0 );
  data( NULL );
  type( DATUM_TYPE_EMPTY );
  return;
}

std::string Datum::serialize( void ) const { // public
  std::string compact;
  std::list<Datum>* l = NULL;
  std::list<Datum>::const_iterator it;
  std::list<Datum>::const_iterator end;
  char foo[16];

  switch ( type() ) {
    case DATUM_TYPE_SIGNED_INT:
      sprintf( foo, "%c%d;", DATUM_SERIAL_SIGNED_INT, *((signed int*)data()) );
      compact.assign( foo );
      break;
    case DATUM_TYPE_CHAR:
      sprintf( foo, "%c%c;", DATUM_SERIAL_CHAR, *((char*)data()) );
      compact.assign( foo );
      break;
    case DATUM_TYPE_STRING:
      sprintf( foo, "%c%u:%s;", DATUM_SERIAL_STRING, strlen((char*)data()), (char*)data() );
      compact.assign( foo );
      break;
    case DATUM_TYPE_EMPTY:
      sprintf( foo, "%c;", DATUM_SERIAL_EMPTY );
      compact.assign( foo );
      break;
    case DATUM_TYPE_ARRAY:
      l = (std::list<Datum>*)data();
      end = l->end();
      sprintf( foo, "%c%u{", DATUM_SERIAL_ARRAY, l->size() );
      compact.assign( foo );
      for ( it = l->begin(); it != end; ++it ) {
        compact.append( it->serialize() );
      }
      compact.append( 1, '}' ).append( 1, ';' );
      break;
    default:
      break;
  }

  return compact;
}

Datum Datum::unserialize( const char* compact, unsigned int start ) { // public
  Datum newbie;
  char* workspace = NULL;
  unsigned int length = 0;
  unsigned int strlen = 0;
  unsigned int open_brace = 0;
  char* colon = 0;
  signed int signed_int = 0;

  switch ( compact[start] ) {
    case DATUM_SERIAL_SIGNED_INT:
      length = (char*)memchr( compact+start, ';', 16 ) - compact+start;
      workspace = new char[length];
      memcpy( workspace, compact+start+1, length );
      sscanf( workspace, "%d", &signed_int );
      newbie = signed_int;
      delete [] workspace;
      break;
    case DATUM_SERIAL_CHAR:
      newbie = compact[start+1];
      break;
    case DATUM_SERIAL_STRING:
      // First we need to figure out the string length...
      colon = (char*)memchr( compact+start, ':', 16 );
      length = colon - compact+start;
      workspace = (char*)calloc( length, sizeof(char) );
      memcpy( workspace, compact+start+1, length );
      sscanf( workspace, "%u", &strlen );
      // Now we can go ahead and actually GET the string (which is strlen bytes long)...
      workspace = (char*)::realloc( workspace, strlen+1 );
      workspace[strlen] = 0;
      memcpy( workspace, colon+1, strlen );
      newbie = workspace;
      free( workspace );
      break;
    case DATUM_SERIAL_ARRAY:
      open_brace = (char*)memchr( compact+start, '{', 16 ) - compact+start;

      break;
    case DATUM_SERIAL_EMPTY:
      break;
    default:
      break;
  }

  return newbie;
}
/*
Datum& Datum::unserialize( const char* compact ) { // public
  unsigned index = 0;
  unsigned length = strlen(compact);
  unsigned foo = 0;
  char bar[32];
  char* buffer = NULL;
  signed int signed_int = 0;
  char char_ = 0;
printf( "compact: %s\n", compact );
  for ( index = 0; index < length; ++index ) {
    switch ( compact[index] ) {
      case DATUM_SERIAL_SIGNED_INT:
        sscanf( compact, "%*c%d;", &signed_int );
        *this = signed_int;
        break;
      case DATUM_SERIAL_CHAR:
        sscanf( compact, "%*c%c;", &char_ );
        *this = char_;
        break;
      case DATUM_SERIAL_STRING:
        // How long is the string suposed to be?
        sscanf( compact, "%*c%u:", &foo );
        // Build our input format string...
        sprintf( bar, "%%*c%%*u:%%%us;", foo );
        // Prepare a new buffer with just the right size...
        buffer = new char[foo+1];
        // Capture the string...
        sscanf( compact, bar, buffer );
        // We're all done...
        *this = buffer;
        // Clean up...
        delete [] buffer;
        break;
      case DATUM_SERIAL_EMPTY:
        clear();
        break;
      case DATUM_SERIAL_ARRAY:
        
        break;
      default:
        break;
    }
  }

  return *this;
}
//*/

void Datum::print( unsigned depth ) const { // public
  std::list<Datum>* l = NULL;
  std::list<Datum>::const_iterator it;
  std::list<Datum>::const_iterator end;

  for ( register unsigned u = 0; u < depth; ++u ) {
    printf( " " );
  }

  switch ( type() ) {
    case DATUM_TYPE_SIGNED_INT:
      printf( "[signed int] = %d", *((signed int*)data()) );
      break;
    case DATUM_TYPE_CHAR:
      printf( "[char] = %c", *((char*)data()) );
      break;
    case DATUM_TYPE_STRING:
      printf( "[string] = %s", (char*)data() );
      break;
    case DATUM_TYPE_EMPTY:
      printf( "[none] = empty" );
      break;
    case DATUM_TYPE_ARRAY:
      printf( "[array] = {\n" );
      l = (std::list<Datum>*)data();
      end = l->end();
      for ( it = l->begin(); it != end; ++it ) {
        it->print( depth + 10 );
      }
      for ( register unsigned u = 0; u < depth + 10; ++u ) {
        printf( " " );
      }
      printf( "}" );
      break;
    default:
      break;
  }

  printf( " <%p>\n", data() );
//  printf( "\n" );

  return;
}

